package org.je.xml.stream;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.je.refl.ClassUtil;
import org.je.xml.Element;
import org.je.xml.XUtil;

/**
 * xml反序列化工具,将xml转换为对象
 * 
 * @author fyf
 */
class ToBean
{

	Object toBean(String xml) {
		if (xml == null)
			return null;

		Element root = XUtil.parse(xml);

		Map<Integer, Object> refMap = new HashMap<Integer, Object>(); // 用于保存已经反序列化的对象

		return xml2Obj(root, refMap);
	}

	@SuppressWarnings("rawtypes")
	private Object xml2Obj(Element ele, Map<Integer, Object> refMap) {
		String tagName = ele.getTagName();

		if ("null".equals(tagName)) { // <null/>标签直接返回null
			return null;
		}

		Integer eleId = Integer.parseInt(ele.getAtt("id"));

		if ("r".equals(tagName)) {
			Object obj = refMap.get(eleId);

			if (obj != null) {
				return obj;
			} else {
				throw new RuntimeException("ref[" + eleId + "] cant find in the refMap");
			}
		}

		String className = StrMap.map2(ele.getAtt("t"));

		Class<?> cl = ClassUtil.getClassByName(className);

		if (ele.getAtt("level") != null) // array type
		{
			int level = Integer.parseInt(ele.getAtt("level"));

			Class<?> realCopClass = realCopClass(cl, level);

			Object array = java.lang.reflect.Array.newInstance(realCopClass, ele.size());

			refMap.put(eleId, array); // save ================================

			add2array(ele, array, refMap);

			return array;
		}

		if (TypeUtil.isBase(cl)) // 基本类型
		{
			if (cl == Date.class) // 日期类型
			{
				Date date = new Date();
				date.setTime(Long.parseLong(ele.getFirst().toString()));
				refMap.put(eleId, date); // 保存起来==============================
				return date;
			} else { // 非日期类型
				Object base = ClassUtil.createObject(cl, ele.getFirst().getValue());

				refMap.put(eleId, base);// 保存起来============================

				return base;
			}
		}

		if (TypeUtil.isStructure(cl)) // 集合类型
		{
			Object bean = ClassUtil.createObject(cl);
			refMap.put(eleId, bean); // 保存 =================================

			if (Map.class.isAssignableFrom(cl)) {
				add2Map((Map) bean, ele, refMap);
			} else {
				add2Collection((Collection) bean, ele, refMap);
			}
			return bean; // 返回
		}

		// ======================= 一般对象类型 ==========================
		Object bean = ClassUtil.createObject(cl);

		refMap.put(eleId, bean);// 保存起来=============================

		List<java.lang.reflect.Field> fList = TypeUtil.getFields(cl, true);

		for (int i = 0; i < ele.size(); ++i) {
			Element cEle = ele.getChild(i);

			String fName = cEle.getAtt("n");

			Object fValue = xml2Obj(cEle, refMap);

			setField(bean, fList, fName, fValue);
		}

		return bean;
	}

	private void add2array(Element ele, Object array, Map<Integer, Object> refMap) {
		for (int i = 0; i < ele.size(); ++i) {
			Element childEle = ele.getChild(i);

			Object obj = xml2Obj(childEle, refMap);

			java.lang.reflect.Array.set(array, i, obj);
		}
	}

	private Class<?> realCopClass(Class<?> copCl, int level) {
		Class<?> cl = copCl;

		for (int i = 1; i < level; ++i) {
			cl = java.lang.reflect.Array.newInstance(cl, 0).getClass();
		}

		return cl;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void add2Map(Map map, Element mapEle, Map<Integer, Object> refMap) {
		for (int i = 0; i < mapEle.size(); ++i) {
			Element entry = mapEle.getChild(i);

			Element keyEle = entry.getFirst("k").getFirst();

			Element valueEle = entry.getFirst("v").getFirst();

			Object key = xml2Obj(keyEle, refMap);

			Object value = xml2Obj(valueEle, refMap);

			map.put(key, value);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void add2Collection(Collection coll, Element collEle, Map<Integer, Object> refMap) {
		for (int i = 0; i < collEle.size(); ++i) {
			Element objEle = collEle.getChild(i);

			Object obj = xml2Obj(objEle, refMap);

			coll.add(obj);
		}
	}

	private void setField(Object bean, List<Field> fList, String fName, Object fValue) {
		for (Field fb : fList) {
			if (fb.getName().equals(fName)) {
				try {
					fb.set(bean, fValue);
				} catch (Exception ex) {
					throw new RuntimeException(ex);
				}
				return;
			}
		}
	}
}
